package com.ifp.opengate.boot.gateway.formatter;

import java.util.*;
import java.util.Map.Entry;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import com.ifp.core.base.Constants;
import com.ifp.core.data.DataElement;
import com.ifp.core.data.DataField;
import com.ifp.core.data.DataList;
import com.ifp.core.data.DataMap;
import com.ifp.core.flow.util.ContextMapChangeUtil;
import com.ifp.core.util.StringUtil;
import com.ifp.gateway.GatewayConstants;
import com.ifp.gateway.bean.GroupMap;
import com.ifp.gateway.bean.MessageDefine;
import com.ifp.gateway.bean.MsgField;
import com.ifp.gateway.bean.MsgList;
import com.ifp.gateway.formatter.AbstractJsonFormatter;

import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;

/**
 * JSON报文格式处理类
 * 
 * @author limh@belink.com
 * @since 2016-07-11
 */

@Component("jsonFormatter")
@Lazy(true)
public class JsonFormatter extends AbstractJsonFormatter<Map, List> {
    @Override
    public Object format(Map headMap, Map dataMap, MessageDefine msgDefine) throws Exception {
        // TODO 如果模板定义为空则返原报文
        if (msgDefine == null) {
            return dataMap;
        }
        StringBuffer msg = new StringBuffer();
        msg.append("{");

        Iterator<Entry<String, DataElement>> elementIter = msgDefine.getElementMap().entrySet().iterator();
        int size = msgDefine.getElementMap().size();
        int index = 0;
        while (elementIter.hasNext()) {
            Entry<String, DataElement> defineEntry = elementIter.next();
            String key = defineEntry.getKey();
            DataElement defineElement = defineEntry.getValue();
            if (defineElement instanceof GroupMap) {
                msg.append("\"").append(key).append("\":{");
                formatGroup(msg, headMap, dataMap, (GroupMap)defineElement);
                index++;
                if (index < size) {
                    msg.append("},");
                } else {
                    msg.append("}");
                }
            } else {
                formatElement(msg, headMap, dataMap, defineElement);
                index++;
                if (index < size) {
                    msg.append(",");
                }
            }
        }

        msg.append("}");
        return msg.toString();
    }

    @Override
    public void formatGroup(StringBuffer msg, Map headMap, Map dataMap, GroupMap groupDefine) throws Exception {
        Iterator<Entry<String, DataElement>> defineIterator = groupDefine.entrySet().iterator();
        int size = groupDefine.size();
        int index = 0;
        while (defineIterator.hasNext()) {
            Entry<String, DataElement> defineEntry = defineIterator.next();
            String key = defineEntry.getKey();
            DataElement defineElement = defineEntry.getValue();
            if (defineElement instanceof GroupMap) {
                msg.append("\"").append(key).append("\":{");
                formatGroup(msg, headMap, dataMap, (GroupMap)defineElement);
                index++;
                if (index < size) {
                    msg.append("},");
                } else {
                    msg.append("}");
                }
            } else {
                formatElement(msg, headMap, dataMap, defineElement);
                index++;
                if (index < size) {
                    msg.append(",");
                }
            }
        }
    }

    public void formatElement(StringBuffer msg, Map headMap, Map dataMap, DataElement defineElement) throws Exception {
        if (defineElement instanceof MsgField) {
            formatField(msg, headMap, dataMap, (MsgField)defineElement);
        } else if (defineElement instanceof MsgList) {
            MsgList listDefine = (MsgList)defineElement;
            if (null != listDefine) {
                String key = listDefine.getName();
                String refName = listDefine.getRefName();
                if (StringUtil.hasText(refName)) {
                    key = refName;
                }
                Object listElement = dataMap.get(key);
                if (null == listElement) {
                    if (listDefine.isNeed()) {
                        throw new Exception(listDefine.getName() + " is need!");
                    } else {
                        return;
                    }
                }
                if (!(listElement instanceof List)) {
                    throw new Exception(key + " is undefine list, here expect of List!");
                } else {
                    formatList(msg, headMap, (List)listElement, listDefine);
                }
            }
        }
    }

    @Override
    public void formatField(StringBuffer msg, Map headMap, Map dataMap, MsgField fieldDefine) throws Exception {
        String key = fieldDefine.getName();
        String value = fieldDefine.getValue();
        String refName = fieldDefine.getRefName();
        if (!StringUtil.hasText(refName)) {
            refName = fieldDefine.getName();
        }

        Object dataElement;
        if (null != fieldDefine.getPattern()
            && fieldDefine.getPattern().equalsIgnoreCase(GatewayConstants.PATTERN_HEAD)) {
            dataElement = headMap.get(refName);
        } else {
            dataElement = dataMap.get(refName);
        }

        if (null == dataElement) {
            if (fieldDefine.isNeed() && !StringUtil.hasText(value)) {
                throw new Exception(fieldDefine.getName() + " is need!");
            }
        } else {
            if (dataElement instanceof DataElement) {
                DataField dataField = (DataField)dataElement;
                if (null != dataField && StringUtil.hasText(dataField.getValue())) {
                    value = dataField.getValue();
                }
            } else if (StringUtil.hasText(dataElement)) {
                value = (String)dataElement;
            }
        }

        if (!fieldDefine.isEmpty() && !StringUtil.hasText(value)) {
            throw new Exception(fieldDefine.getName() + " is empty!");
        }

        // 拼装报文
        value = value == null ? "" : value;
        msg.append("\"").append(key).append("\":\"").append(StringUtil.formatJSONText(value)).append("\"");
    }

    @Override
    public void formatList(StringBuffer msg, Map headMap, List dataList, MsgList listDefine) throws Exception {
        Object[] fieldDefineArray = listDefine.values().toArray();
        msg.append("\"").append(listDefine.getName()).append("\":[");
        int size = dataList.size();
        int index = 0;
        int currentIndex = 0;
        for (Object rowElement : dataList) {
            currentIndex = 0;
            if (rowElement instanceof Map) {
                msg.append("{");
                Map rowMap = (Map)rowElement;
                for (Object defineObject : fieldDefineArray) {
                    int currentSize = fieldDefineArray.length;
                    formatElement(msg, headMap, rowMap, (DataElement)defineObject);
                    currentIndex++;
                    if (currentIndex < currentSize) {
                        msg.append(",");
                    }
                }
                index++;
                if (index < size) {
                    msg.append("},");
                } else {
                    msg.append("}");
                }
            } else {
                throw new Exception("List rows expect of Map");
            }
        }
        msg.append("]");
    }

    @Override
    public void unformat(Map headMap, Map dataMap, String recMsg, MessageDefine msgDefine) throws Exception {
        JSONObject jsonObject = JSONObject.fromObject(recMsg);
        // TODO 如果模板定义为空则原路返回
        if (msgDefine == null) {
            ContextMapChangeUtil.copyDataMap(jsonObject, dataMap);
            return;
        }
        Iterator<Entry<String, DataElement>> elementIter = msgDefine.getElementMap().entrySet().iterator();
        while (elementIter.hasNext()) {
            Entry<String, DataElement> elementEntry = elementIter.next();
            String key = elementEntry.getKey();
            DataElement defineElement = elementEntry.getValue();
            if (defineElement instanceof GroupMap) {
                GroupMap defineGroup = (GroupMap)defineElement;
                JSONObject groupObject = jsonObject.getJSONObject(key);
                String type = defineGroup.getType();
                if (null != type && type.equals("head")) {
                    unformatGroup(headMap, headMap, groupObject, defineGroup);
                } else {
                    unformatGroup(headMap, dataMap, groupObject, defineGroup);
                }
            } else {
                unformatElement(headMap, dataMap, jsonObject.get(defineElement.getName()), defineElement);
            }
        }
    }

    @Override
    public void unformatGroup(Map headMap, Map outDataMap, JSONObject groupObject, GroupMap defineMap)
        throws Exception {
        if (groupObject.isNullObject()) {
            if (defineMap.isNeed()) {
                throw new Exception(defineMap.getName() + " is need!");
            } else {
                return;
            }
        }

        Iterator<DataElement> defineIterator = defineMap.values().iterator();
        while (defineIterator.hasNext()) {
            DataElement defineElement = defineIterator.next();
            if (defineElement instanceof GroupMap) {
                unformatGroup(headMap, outDataMap, groupObject.getJSONObject(defineElement.getName()),
                    (GroupMap)defineElement);
            } else {
                unformatElement(headMap, outDataMap, groupObject.get(defineElement.getName()), defineElement);
            }
        }
    }

    public void unformatElement(Map headMap, Map outDataMap, Object object, DataElement defineElement)
        throws Exception {
        if (defineElement instanceof MsgField) {
            MsgField msgField = (MsgField)defineElement;
            if (null == object) {
                if (msgField.isNeed()) {
                    throw new Exception(defineElement.getName() + " is need!");
                } else {
                    return;
                }
            }
            if ((object instanceof JSONNull) || (Constants.NULL.equals(object.toString().trim()))) {
                // 解决 "{\"id\":null}"、 "{\"id\":\"null\"}"的问题，JSONNull 对这两种格式都没用 liangmin
                unformatField(headMap, outDataMap, "", msgField);
            } else {
                // 解决 "{\"id\":77}"等的问题，数值类型不能用(String)object强制转换，既然object非空则没有必要再抛异常了 liangmin
                unformatField(headMap, outDataMap, object.toString(), msgField);
            }

            /*if(object instanceof String){
            	unformatField(headMap, outDataMap, (String)object, msgField);
            }else if(object instanceof JSONNull){
            	unformatField(headMap, outDataMap, "", msgField);
            }else{
            	throw new Exception("message define " + defineElement.getName() + ", expect for a field!");
            }*/
        } else if (defineElement instanceof MsgList) {
            MsgList listDefine = (MsgList)defineElement;
            if (null == object) {
                if (listDefine.isNeed()) {
                    throw new Exception(defineElement.getName() + " is need!");
                } else {
                    return;
                }
            }

            if (object instanceof JSONArray) {
                unformatList(headMap, outDataMap, (JSONArray)object, listDefine);
            } else if ((object instanceof JSONNull) || (Constants.NULL.equals(object.toString().trim()))) {
                // 解决 "{\"id\":null}"、 "{\"id\":\"null\"}"的问题，JSONNull 对这两种格式都没用 liangmin
                unformatList(headMap, outDataMap, new JSONArray(), listDefine);
            } else {
                throw new Exception("message define " + defineElement.getName() + ", expect for a list!");
            }
        }
    }

    @Override
    public void unformatField(Map headMap, Map outDataMap, String jsonValue, MsgField fieldDefine) throws Exception {
        if (!fieldDefine.isEmpty() && !StringUtil.hasText(jsonValue)) {
            throw new Exception(fieldDefine.getName() + " is empty!");
        }

        String key = fieldDefine.getName();
        String value = jsonValue;
        String defaultValue = fieldDefine.getValue();
        String refName = fieldDefine.getRefName();
        if (StringUtil.hasText(refName)) {
            key = refName;
        }

        if (null != defaultValue && !StringUtil.hasText(value)) {
            value = defaultValue;
        }

        value = value == null ? "" : value;
        if (null != fieldDefine.getPattern()
            && fieldDefine.getPattern().equalsIgnoreCase(GatewayConstants.PATTERN_HEAD)) {
            StringUtil.setValue(headMap, key, value);
        } else {
            StringUtil.setValue(outDataMap, key, value);
        }
    }

    @Override
    public void unformatList(Map headMap, Map outDataMap, JSONArray jsonArray, MsgList listDefine) throws Exception {
        Object[] fieldDefineArray = listDefine.values().toArray();
        String key = listDefine.getName();
        String refName = listDefine.getRefName();
        if (StringUtil.hasText(refName)) {
            key = refName;
        }

        if (outDataMap instanceof DataMap) {
            DataList dList;
            DataMap outMap = (DataMap)outDataMap;
            // 原map里是否有，如果没有则新建
            if (outMap.containsKey(key)) {
                DataElement outElement = outMap.get(key);
                if (outElement instanceof DataList) {
                    dList = (DataList)outElement;
                } else {
                    throw new Exception(key + " is define list, expect of DataList in context here!");
                }
            } else {
                dList = new DataList();
                dList.setDefineMap((DataMap)outMap.getDefineMap().get(key));
            }

            for (int j = 0; j < jsonArray.size(); j++) {
                DataMap dMap = dList.createSubDataMap();
                JSONObject jObject = jsonArray.getJSONObject(j);
                for (int i = 0; i < fieldDefineArray.length; i++) {
                    DataElement defineElement = (DataElement)fieldDefineArray[i];
                    unformatElement(headMap, dMap, jObject.get(defineElement.getName()), defineElement);
                }
                dList.add(dMap);
            }

            outDataMap.put(key, dList);
        } else {
            List dList;
            // 原map里是否有，如果没有则新建
            if (outDataMap.containsKey(key)) {
                Object outElement = outDataMap.get(key);
                if (outElement instanceof List) {
                    dList = (List)outElement;
                } else {
                    throw new Exception(key + " is define list, expect of List in context here!");
                }
            } else {
                dList = new ArrayList();
            }

            for (int j = 0; j < jsonArray.size(); j++) {
                Map dMap = new LinkedHashMap();
                JSONObject jObject = jsonArray.getJSONObject(j);
                for (int i = 0; i < fieldDefineArray.length; i++) {
                    DataElement defineElement = (DataElement)fieldDefineArray[i];
                    unformatElement(headMap, dMap, jObject.get(defineElement.getName()), defineElement);
                }
                dList.add(dMap);
            }

            outDataMap.put(key, dList);
        }
    }

}
